"""
__author__ = Zdenek Maxa

Wrapper class for logging module (standard Python module).

"""


import sys
import traceback
import linecache
import logging
import pprint



class Logger(logging.getLoggerClass()):
    """Customised Logger. Logging either to console or into a file."""

    def __init__(self, name = "Logger", logFile = None, level = logging.DEBUG):
        """"""

        # handler for logging into a file, optional
        self._logFileHandler = None

        # initialise logger, necessary!
        logging.Logger.__init__(self, name)

        # should be set for further created handlers
        self.setLevel(level)

        # %(name)-12s gives name as given here: name = "Logger"
        #fs = "%(asctime)s %(name)-8s %(levelname)-9s %(message)s"
        fs = "%(levelname)-9s %(asctime)s %(name)-8s %(message)s"
        formatter = logging.Formatter(fs)

        if not logFile:
            # logging to console, sys.stdout
            console = logging.StreamHandler(sys.stdout)
            console.setFormatter(formatter)
            self.addHandler(console)
        else:
            # log file has been specified, log into file rather than console
            self._logFileHandler = logging.FileHandler(logFile)
            self._logFileHandler.setLevel(level)
            self._logFileHandler.setFormatter(formatter)
            self.addHandler(self._logFileHandler)

        # should be first logging message
        level = logging.getLevelName(self.getEffectiveLevel())
        
        self.pp = pprint.PrettyPrinter(indent = 4)
        
        self.warn("Logger instance initialised (level: %s, log file: %s)\n%s" %
                  (level, logFile, 78 * '='))



    def close(self):
        # can't be put into __del__() - gives error (file already closed)
        self.warn("Logger closing.\n\n\n")
        if self._logFileHandler:
            self._logFileHandler.flush()
            self._logFileHandler.close()



    def getTracebackSimple(self):
        """Returns formatted traceback of the most recent exception."""

        # sys.exc_info() most recent exception
        trace = traceback.format_exception(*sys.exc_info())
        tbSimple = "".join(trace)  # may want to add '\n'
        
        return tbSimple



    def getTracebackComplex(self, localsLevels = 5):
        """Returns formatted traceback of the most recent exception.
           Could write into a file-like object (argument would be
           output = sys.stdout), now returns result in formatted string.
        """
        tbComplex = "".join([78 * '-', '\n'])
        tbComplex = "".join([tbComplex, "Problem: %s\n" % sys.exc_info()[1]])
        
        trace = sys.exc_info()[2]
        stackString = []
        while trace is not None:
            frame = trace.tb_frame
            lineno = trace.tb_lineno
            code = frame.f_code
            filename = code.co_filename
            function = code.co_name
            if filename.endswith(".py"):
                line = linecache.getline(filename, lineno)
            else:
                line = None
            stackString.append((filename, lineno, function, line, frame))
            trace = trace.tb_next

        tbComplex = "".join([tbComplex, "Traceback:\n"])
        localsLevel = max(len(stackString) - localsLevels, 0)
        for i in range(len(stackString)):
            (filename, lineno, name, line, frame) = stackString[i]
            outputLine = (" File '%s', line %d, in %s (level %i):\n" %
                         (filename, lineno, name, i))
            tbComplex = "".join([tbComplex, outputLine])
            if line:
                tbComplex = "".join([tbComplex, "  %s\n" % line])
            if i >= localsLevel:
                # want to see complete stack if exception came from a template
                pass
        
        tbComplex = "".join([tbComplex, 78 * '-', '\n'])

        del stackString[:]
        frame = None
        trace = None

        return tbComplex



    def error(self, msg, traceBack = False):
        if traceBack:
            # get last exception traceback
            # add traceback below the normal 'msg' message
            msg = ("%s\n\n%s" % (msg, self.getTracebackSimple()))
        logging.Logger.error(self, msg)



    def fatal(self, msg, traceBack = False):
        if traceBack:
            # get last exception traceback
            # add traceback below the normal 'msg' message
            msg = ("%s\n\n%s" % (msg, self.getTracebackSimple()))
        logging.Logger.fatal(self, msg)



    def __del__(self):
        pass